package ru.ifmo.rybakov.muclumon.pes;

import java.io.StringReader;
import java.text.DateFormat;
import java.util.Calendar;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.ifmo.rybakov.muclumon.pes.sr.SequenceRunType;
import ru.ifmo.rybakov.muclumon.pes.sri.SequenceRunInfo;

@WebService(portName = "PESListenerPort", targetNamespace="urn:ru:ifmo:rybakov:muclumon:pes")
public class PESListener {
	
	private static final Logger logger = LoggerFactory.getLogger(PESListener.class);
	
	public PESListener() {
		logger.debug("version:1.0");
	}
	
	private JAXBContext sequenceRunInfoJAXBContext;
	private Unmarshaller sequenceRunInfoUnmarshaller;
	private JAXBContext sequenceRunJAXBContext;
	private Unmarshaller sequenceRunUnmarshaller;
	
	@WebMethod(action = "urn:ru:ifmo:rybakov:muclumon:pes#notify")
	public String notify(
			@WebParam(name = "eventType") String eventType,
			@WebParam(name = "sequenceRunInfoXml") String sequenceRunInfoXml,
			@WebParam(name = "sequenceRunXml") String sequenceRunXml,
			@WebParam(name = "timestamp") Calendar timestamp) {
		logger.debug("On notify() invoked");
		// check if event type was provided
		if (eventType == null) {
			logger.warn("No event type provided, can not process request");
			return "No event type provided, can not process request";
		}
		
		SequenceRunInfo sequenceRunInfo = parseSequenceRunInfo(sequenceRunInfoXml);
		SequenceRunType sequenceRun = parseSequenceRun(sequenceRunXml);
		
		
		EventType event;
		try {
			event = EventType.valueOf(eventType);
		} catch (IllegalArgumentException e) {
			logger.warn("Unknown event type provided, skipping");
			return "Unknown event type";
		}
		
		// create revision
		PESHandler.handle(event, sequenceRunInfo, sequenceRun);
		
		return eventType + " (" + DateFormat.getDateInstance().format(timestamp.getTime()) + ")";
	}
	
	private SequenceRunInfo parseSequenceRunInfo(String sequenceRunInfoXml) {
		if (sequenceRunInfoXml != null) {
//			System.out.println(sequenceRunInfoXml);
			try {
				if (sequenceRunInfoJAXBContext == null) {
					sequenceRunInfoJAXBContext = JAXBContext.newInstance("ru.ifmo.rybakov.muclumon.pes.sri");
					sequenceRunInfoUnmarshaller = sequenceRunInfoJAXBContext.createUnmarshaller();
				}
				SequenceRunInfo result = (SequenceRunInfo) sequenceRunInfoUnmarshaller.unmarshal(new StringReader(sequenceRunInfoXml));
				logger.debug("Sequence run info xml data was successfully processed");
				return result;
			} catch (Exception e) {
				logger.error("Sequence run info xml processing failed", e);
			}
		}
		return null;
	}
	
	@SuppressWarnings("unchecked")
	private SequenceRunType parseSequenceRun(String sequenceRunXml) {
		if (sequenceRunXml != null) {
//			System.out.println(sequenceRunXml);
			try {
				if (sequenceRunJAXBContext == null) {
					sequenceRunJAXBContext = JAXBContext.newInstance("ru.ifmo.rybakov.muclumon.pes.sr");
					sequenceRunUnmarshaller = sequenceRunJAXBContext.createUnmarshaller();
				}
				SequenceRunType result = (SequenceRunType) ((JAXBElement) sequenceRunUnmarshaller.unmarshal(new StringReader(sequenceRunXml))).getValue();
				logger.debug("Sequence run xml data was successfully processed");
				return result;
			} catch (Exception e) {
				logger.error("Sequence run xml processing failed", e);
			}
		}
		return null;
	}
}
